package com.jxpanda.commons.base;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonIncludeProperties;
import lombok.*;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * 这次的设计思路直接扩展mybatis-plus的类
 *
 * @author Panda
 */
@NoArgsConstructor
@JsonIncludeProperties({"current", "pages", "size", "total", "records", "next", "hasNext"})
public class Pagination<T> extends Page<T> {

    /**
     * 提供int类型的构造器，用起来方便一点
     */
    public Pagination(int current, int size) {
        super(current, size);
    }

    /**
     * 返回下一页的页码
     */
    public long getNext() {
        return (getCurrent() * getSize() < getTotal()) ? getCurrent() + 1 : getCurrent();
    }

    /**
     * 返回是否有下一页
     */
    public boolean getHasNext() {
        return getNext() > getCurrent();
    }


    /**
     * 内存分页
     *
     * @param current 当前页码
     * @param size    每页长度
     * @param data    数据列表
     * @return 计算结束后的分页对象
     */
    public static <T> Pagination<T> paging(int current, int size, List<T> data) {

        // 总数据长度
        var total = data.size();
        // 计算起始偏移值
        var startOffset = Math.max((current - 1) * size, 0);
        // 计算结束偏移值
        var endOffset = Math.min(startOffset + size, total);
        // 计算共有多少页
        var pages = total / size + (total % size > 0 ? 1 : 0);
        // 使用计算好的偏移值，内存分页
        var records = startOffset > endOffset ? new ArrayList<T>() : data.subList(startOffset, endOffset);

        // 创建对象，设置各值
        var pagination = new Pagination<T>(current, size);
        pagination.setPages(pages);
        pagination.setTotal(total);
        pagination.setRecords(records);
        return pagination;
    }

    /**
     * 对象拷贝，因为要支持更改泛型，所以不用内置clone()函数
     */
    private <E> Pagination<E> duplicate(List<E> records) {
        return new Pagination<E>().apply(pagination -> {
            pagination.setRecords(records);
            pagination.setCurrent(this.getCurrent());
            pagination.setPages(this.getPages());
            pagination.setTotal(this.getTotal());
            pagination.setOrders(this.getOrders());
            // size使用实际的size
            pagination.setSize(records.size());
        });
    }

    public <R> Pagination<R> replace(List<R> records) {
        return duplicate(records);
    }

    public Pagination<T> filter(Predicate<? super T> predicate) {
        return duplicate(getRecords().stream().filter(predicate).collect(Collectors.toList()));
    }

    public <R> Pagination<R> map(Function<? super T, ? extends R> transform) {
        return duplicate(getRecords().stream().map(transform).collect(Collectors.toList()));
    }

    public Pagination<T> distinct() {
        return duplicate(getRecords().stream().distinct().collect(Collectors.toList()));
    }

    public Pagination<T> distinctBy(Function<? super T, ?> keyExtractor) {
        return duplicate(getRecords().stream().filter(predicate(keyExtractor)).collect(Collectors.toList()));
    }

    public Pagination<T> apply(Consumer<? super Pagination<T>> action) {
        action.accept(this);
        return this;
    }

    public Pagination<T> sorted(Comparator<? super T> comparator) {
        // 排序不会让元素的数量变化，所以不使用duplicate函数
        this.records.sort(comparator);
        return this;
    }

    private static <T> Predicate<? super T> predicate(Function<? super T, ?> keyExtractor) {
        Map<Object, Boolean> seen = new ConcurrentHashMap<>(16);
        return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
    }

}
